package top.yangguangmc.sunshine_anticheat.check;

import com.github.retrooper.packetevents.PacketEvents;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import top.yangguangmc.sunshine_anticheat.check.action.Action;
import top.yangguangmc.sunshine_anticheat.check.checks.combat.AutoClickerCheck;
import top.yangguangmc.sunshine_anticheat.check.checks.combat.AutoWeaponCheck;
import top.yangguangmc.sunshine_anticheat.check.checks.player.*;
import top.yangguangmc.sunshine_anticheat.check.checks.world.AirPlaceCheck;
import top.yangguangmc.sunshine_anticheat.check.checks.world.FastBreakCheck;
import top.yangguangmc.sunshine_anticheat.events.ServerTickEvent;
import top.yangguangmc.sunshine_anticheat.utils.Timer;
import top.yangguangmc.sunshine_anticheat.utils.Utils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Level;

import static top.yangguangmc.sunshine_anticheat.utils.Builtin.*;

public class CheckManager implements Listener {
    private final List<Check> checks = new ArrayList<>();
    private final Timer vlResetTimer = new Timer();

    public CheckManager() {
        checks.add(new AntiFireballCheck());
        checks.add(new AutoRespawnCheck());
        checks.add(new EagleCheck());
        checks.add(new AutoToolCheck());
        checks.add(new AutoWeaponCheck());
        checks.add(new AirPlaceCheck());
        checks.add(new FastBreakCheck());
        checks.add(new AutoClickerCheck());
        checks.add(new DeathActionCheck());

        checks.sort(Comparator.comparing(Check::getCheckName));
    }

    public void init() {
        loadConfig();
        Bukkit.getPluginManager().registerEvents(this, ssac);
        for (Check check : checks) {
            Bukkit.getPluginManager().registerEvents(check, ssac);
            PacketEvents.getAPI().getEventManager().registerListener(check, check.getPriority());
        }
    }

    public List<Check> getChecks() {
        return checks;
    }

    public List<Check> getChecksByCategory(CheckCategory category) {
        List<Check> checkList = new ArrayList<>();
        for (Check check : checks) {
            if (check.getCategory() == category) checkList.add(check);
        }
        return checkList;
    }

    @SuppressWarnings("unchecked")
    public <T extends Check> T getCheckByClass(Class<T> checkClass) {
        for (Check check : checks) {
            if (check.getClass() == checkClass) {
                return (T) check;
            }
        }
        return null;
    }

    public void loadConfig() {
        for (Check check : checks) {
            String path = check.getConfigKeyPath() + ".";
            for (Field field : Utils.getFields(check)) {
                CheckSetting ann = field.getAnnotation(CheckSetting.class);
                if (ann == null) continue;
                try {
                    field.setAccessible(true);
                    Object oldValue = field.get(check);
                    if (oldValue instanceof Boolean) {
                        field.set(check, ssac.getConfigManager().getBoolean(path + ann.configName()));
                    } else if (oldValue instanceof Integer) {
                        field.set(check, ssac.getConfigManager().getInt(path + ann.configName()));
                    } else if (oldValue instanceof Double) {
                        field.set(check, ssac.getConfigManager().getDouble(path + ann.configName()));
                    } else if (oldValue instanceof String) {
                        field.set(check, ssac.getConfigManager().getString(path + ann.configName()));
                    } else if (oldValue instanceof List) {
                        if (ann.configName().equals("actions") && (((List<?>) oldValue).isEmpty() || ((List<?>) oldValue).get(0) instanceof Action)) {
                            check.setActions(ssac.getConfigManager().getActionList(path + ann.configName()));
                        } else field.set(check, ssac.getConfigManager().getList(path + ann.configName()));
                    } else {
                        logger.warning("Illegal type of setting: " + path + ann.configName()
                                + "! Please report this to the developers!");
                    }
                } catch (Exception e) {
                    logger.log(Level.WARNING, "Failed to load settings for " + path + ann.configName(), e);
                }

            }
        }
    }

    @EventHandler
    public void onTick(ServerTickEvent event) {
        for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
            for (Check check : checks) {
                check.getViolations().putIfAbsent(onlinePlayer.getUniqueId(), 0);
            }
        }
        if (ssac.getConfigManager().getBoolean("vl-resetting.reset-by-interval") &&
                vlResetTimer.hasTimePassed(ssac.getConfigManager().getInt("vl-resetting.reset-interval") * 1000L)) {
            vlResetTimer.reset();
            resetVl();
            ssac.getAlertManager().send("Violations for all online players has been reset!");
        }
    }

    @EventHandler
    public void onPlayerDeath(PlayerDeathEvent event) {
        if (ssac.getConfigManager().getBoolean("vl-resetting.reset-on-death")) {
            resetVl(event.getEntity().getName());
        }
    }

    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent event) {
        if (ssac.getConfigManager().getBoolean("vl-resetting.reset-on-rejoin")) {
            resetVl(event.getPlayer().getName());
        }
    }

    public void resetVl() {
        for (Check check : checks) {
            check.resetViolations();
        }
    }

    public void resetVl(String player) {
        Player player1 = Bukkit.getPlayer(player);
        if (player1 == null) throw new IllegalArgumentException("Cannot find player " + player + "!");
        for (Check check : checks) {
            check.resetViolation(player1);
        }
    }
}
